home *** CD-ROM | disk | FTP | other *** search
/ Freaks Macintosh Archive / Freaks Macintosh Archive.bin / Freaks Macintosh Archives / Textfiles / zines / DNA / DNAV2I2.sit / DNAV2I2 / DNA202.004 < prev    next >
Text File  |  1998-03-02  |  42KB  |  846 lines

  1.                       IBM PC Key Recording
  2.                                by
  3.                            Zephyr 6/95
  4.  
  5. After several months spent having a life, I'm back to do a hacker
  6. article or two. What sparked my interest was the publishing of
  7. the 2600 columns on keyboard recording. While they both mentioned
  8. several nice tips on how to write a keyboard recording program,
  9. neither one of them was particularly detailed, and both offered
  10. the programs at a price. Obviously, being a one time hacker
  11. myself, I wasn't about to pay for anything I could get myself.
  12. Now, I managed to find a couple of key recording programs out
  13. there, some written in assembly, some in C. Quite frankly,
  14. however, none of them worked well enough, or was small enough to
  15. satisfy me. I figured I could write a much better program than
  16. any of the ones currently available. The smallest one I saw came
  17. down to over 1k when assembled into a .COM file. The few that had
  18. source looked like my 'crack baby sister'(tm) wrote them.
  19. Inefficient and bulky to say the least. If I could make my own
  20. version that was smaller and quicker, it would certainly be
  21. better for masking it's effect in the background. Naturally, I
  22. wouldn't want people to notice their machines getting any slower,
  23. or their available RAM suddenly dropping if I installed this on
  24. their machines. After a week of on and off programming, I was
  25. successful. My finished product was an encrypted, stealthy TSR
  26. weighing in at under 512 bytes for the code. In writing this
  27. program I learned a lot about the DOS and the PC itself; that is
  28. what I intend to share with you.
  29.      I'm assuming you have some knowledge of the Intel x86 series
  30. of microprocessors; understanding basic data structures like
  31. stacks and buffers is a must. I also assume you have a basic
  32. understanding of assembly language, and the theories behind it.
  33. Without that knowledge, this text will likely be useless to you.
  34. If you're really interested in knowing how to write interesting
  35. programs such as this on your own, I suggest you start learning
  36. low level programming, and come back to this file later.
  37.      Here is the typical disclaimer. Obviously, the information
  38. in this article could easily be used to make a program that could
  39. be used in a manner that would break certain laws. I do not
  40. condone illegal activity, and I do not want others to assume that
  41. because they have the tools necessary to break security that they
  42. are somehow obligated to. Anything done with this information is
  43. done at your own risk. Please act with discretion.
  44.      First off, I am going to describe how the x86 line of
  45. computers handles code and interrupts so that it will be easier
  46. to explain what I did with it. Obviously, the CPU executes
  47. instructions from memory, and typically it does it in order: one
  48. instruction then the next and so on. Sure, there are jumps and
  49. calls, but they all act like a normal programming language would.
  50. They simply direct the flow of execution within a program.
  51. Another condition exists that changes the flow of execution, and
  52. it has nothing to do with the currently running program. Any time
  53. the CPU gets something called an interrupt, it stops executing
  54. the code it was working on, jumps into an interrupt handler, and
  55. returns to the original code when it's finished running the
  56. handler. The handling routine is often called an ISR(Interrupt
  57. Service Routine). The ISR is a routine that does some sort of
  58. work that needed to be done. When does an interrupt hit the
  59. processor? Most of the hardware interrupts can hit at any time.
  60. For instance, if an interrupt 9h came into the CPU, it would mean
  61. that a key was ready to be read from the keyboard to be put in
  62. the keyboard buffer. Once the interrupt 9 ISR is done putting the
  63. keystroke in the buffer, it returns to the program that was
  64. interrupted. Where are 'interrupts' generated? There are two
  65. sources: it can come from hardware or software. The hardware
  66. interrupts are things like the keyboard controller issuing an
  67. interrupt 9h, to indicate that a key is ready to be read.
  68. Software interrupts are things like DOS call int 21h, which is
  69. the main DOS service call. How does the CPU know where to look
  70. for the ISR routine? It looks at something called the interrupt
  71. vector table(IVT). The IVT has a series of 256 far pointers to
  72. the place in memory where the ISR routines are. If you were to
  73. change any of these pointers, the interrupt would then jump to
  74. another place in memory(which you can then specify). The fact
  75. that you can change the IVT means that you can make your own code
  76. into an ISR. You can make the keyboard interrupt execute code
  77. that you have specified, for instance, AND it will execute only
  78. when a keystroke comes into the computer no matter what your
  79. computer happens to be doing at that moment(reading the HD,
  80. writing to the screen etc.) Once your code has finished doing
  81. it's work, you can call the code that the interrupt originally
  82. wanted to call - the BIOS routine that handles keystrokes.
  83. Basically, you insert yourself as another link in the chain of
  84. events. This makes a lot of sense for our program, since we
  85. really don't want to be executing our key capturing code if
  86. nothing is being input on the keyboard. If we can chain into
  87. interrupts, we can then grab keys only when they're being input.
  88. Because ISR routines can be called at any time, we have to make
  89. sure that our ISR doesn't modify anything that would crash the
  90. computer, such as flags, registers and key data structures used
  91. by DOS or BIOS. I'll address that later.
  92.      For our key grabbing interrupt, I decided to chain software
  93. interrupt 16h. This is the BIOS routine that every DOS program
  94. I've ever seen calls to get keyboard input. There is another
  95. possibility here; I could chain into interrupt 9h, which is the
  96. hardware keyboard interrupt. However, it is substantially more
  97. difficult to decipher int 9h, and it's genuinely not necessary
  98. either. Interrupt 16h pulls data out of the keyboard buffer and
  99. gives it in usable form to the program that asked for it.
  100. Interrupt 9h, on the other hand, takes info directly from the
  101. keyboard and puts it into the keyboard buffer for interrupt 16h
  102. to pull back out later. Interrupt 16h gives us the advantage that
  103. we only get the keystrokes that the software asking for
  104. keystrokes got. If we intercepted interrupt 9h, we would get
  105. every keystroke, even if it overran the keyboard buffer or
  106. something else that stopped the program from receiving input. Our
  107. interrupt 16h handler is probably the easiest part of the
  108. program. All it has to do is this: figure out if the interrupt
  109. that's coming in is one of the two which actually pulls keys from
  110. the keyboard buffer(AH=1h or 10h). It either has to execute the
  111. original ISR if it's not one of our two interrupts, or put the
  112. keystroke in the buffer for later saving to disk. Let's try some
  113. pseudocode:
  114.  
  115. Interrupt 16h is called, and CPU jumps to our code...
  116. Save all registers and flags
  117. IF AH != 0 or 10h
  118. {
  119. Execute old interrupt 16h
  120. Restore all registers and flags
  121. exit
  122. }
  123. ELSE
  124. {
  125. Execute old interrupt 16h
  126. Get returned key value from the interrupt
  127. Get address of the top of the keyboard saving buffer from
  128. variable
  129. Put key value in the buffer
  130. IF buffer is full, call our new int 21h handler to write it to
  131. disk
  132. Increment the top of buffer variable
  133. restore all registers and flags
  134. exit
  135. }
  136.  
  137.      Not difficult at all is it? Your typical ISR routine, which
  138. except for the call if the buffer is full only modifies it's own
  139. memory space. For now, we're not going to deal with the disk
  140. writing call from interrupt 16h, since it requires more depth of
  141. explanation than I can provide just yet. Now, I'm going to get
  142. into the interrupt 21h ISR routine. I'm going to make it so every
  143. time an interrupt 21h comes along, all the keystrokes that have
  144. been saved in the buffer will be written to disk. (Actually I
  145. changed the program later so that you could specify a minimum
  146. number of keystrokes before a save happened. There is a character
  147. input function provided by DOS, and if it's being used by a
  148. program, you'll get a disk write every time you hit a key, which
  149. is not desirable.) The reason I'm chaining into interrupt 21h is
  150. twofold. First off, I don't want my program to write a keystroke
  151. to disk EVERY time a key is pressed, only when something else is
  152. going on in the first place. For instance, if someone types 'dir'
  153. at the commandline, the three letters and the return are put into
  154. the keyboard buffer, waiting to be written to disk. When DOS
  155. tries to search for files, using interrupt 21h, it executes my
  156. code first, thereby writing my keystrokes to disk before doing
  157. the original work the int 21h was supposed to do(search for files
  158. in the current directory). So, this hides the operation of the
  159. keyboard recorder from wary users who might suspect something if
  160. their HD light went on every time they hit a key. The other
  161. reason for chaining interrupt 21h for writing keys to disk is
  162. that it solves many DOS reentrancy issues. One of the most
  163. annoying problems DOS has(besides having a real mode 20 bit
  164. addressing space, and not multitasking) is that it's functions
  165. are not reentrant. They are called serially reusable, in that
  166. they can't be called more than once at the same time. This
  167. normally isn't a problem in your typical program, but for us it
  168. is. From a single program, it's impossible to call interrupt 21h
  169. more than once. However, if you've chained into another interrupt
  170. that stops the CPU in the middle of servicing an interrupt 21h,
  171. and then try to open a file, you'll likely crash your computer,
  172. since you've now entered int 21h twice. I won't go into why you
  173. can't use the DOS interrupts more than once at a time, just know
  174. it's just not a good idea. You will crash your system quickly if
  175. you do. There are ways of calling functions more than once, if
  176. they don't use the DOS stack, or if they're using different DOS
  177. stacks. However, it's difficult to be sure of these things, so I
  178. take a safer method for ensuring there aren't reentrancy
  179. problems. By trapping int 21h, we are assured that we are not
  180. doing file writes during the DOS int 21h ISR, because we get
  181. control before it does. Pseudocode:
  182.  
  183. Interrupt 21h entrypoint:
  184. Save all registers and flags
  185. IF buffer is empty jump to END
  186. Encrypt keystrokes in buffer
  187. Open file for output
  188. Move file pointer to end of file
  189. Write our keystrokes to file
  190. Close file
  191. Restore all registers and flags
  192. IF we've been called from interrupt 16h, return there
  193. END:
  194. Jump to old interrupt 21h ISR
  195. Exit
  196.  
  197. Also a very straightforward routine, although when implemented,
  198. it is larger than the much simpler interrupt 16h handler. The
  199. last line is a jump to the old interrupt 21h. We want to jump to
  200. it as the last instruction so it returns directly from the
  201. calling code, and also because we don't have to modify the flags
  202. on the stack. Modifying the flags takes time, and takes up code
  203. size we don't want to spare. In case you're not familiar with how
  204. the stack works in relation to interrupts, let me illustrate:
  205. First of all, a stack works like a stack of plates in a
  206. cafeteria. When you want a plate, the only one you can grab is
  207. the top one. Same with the stack. Although you can save lots of
  208. info on the stack, you can only pull the top piece off. For
  209. instance, if we push three values onto the stack like so:
  210.  
  211. PUSH A
  212. PUSH B
  213. PUSH C
  214.  
  215. On the stack it looks like this:
  216.      +---------+
  217.      |    C    |     <----- Top of stack
  218.      +---------+
  219.      |    B    |
  220.      +---------+
  221.      |    A    |
  222.      +---------+
  223.      |         |
  224.      .         .
  225.      .         .
  226.  
  227. The last value pushed is the only one available, so to get back
  228. the data back in the right order, you have to pop them off the
  229. stack thusly:
  230.  
  231. POP  C
  232. POP  B
  233. POP  A
  234.  
  235. Now that we have the elementary stack theory out of the way,
  236. let's look at how interrupts use the stack. When an interrupt is
  237. called, it jumps to execute ISR code, as we know. But, how does
  238. it know where to return when it's done executing the ISR? An
  239. 'IRET' instruction is issued, which returns to the calling code.
  240. Where does it get the info it needs to return? Well the answer is
  241. obviously the stack. When an int instruction is issued, the
  242. processor pushes the current flags onto the stack and then pushes
  243. the current address onto the stack after it:
  244.  
  245. INT = PUSH FLAGS + PUSH ADDRESS
  246.  
  247.      +---------+
  248.      |INT ADDR |    <----- Top of stack
  249.      +---------+
  250.      |  FLAGS  |
  251.      +---------+
  252.      |         |
  253.      .         .
  254.      .         .
  255.  
  256. This is the data that the IRET instruction needs to return to
  257. where the INT was called in the original code. IRET simply pops
  258. the address off the stack returns there, and then pops the flags
  259. off the stack to put them back to their original conditions.
  260.  
  261. When we execute a 'CALL' from inside an ISR, it places another
  262. return address onto the stack:
  263.  
  264.      +---------+
  265.      |CALL ADDR|
  266.      +---------+
  267.      |INT ADDR |
  268.      +---------+
  269.      |  FLAGS  |
  270.      +---------+
  271.      |         |
  272.      .         .
  273.      .         .
  274.  
  275.      Before we execute the IRET instruction, we have to make sure
  276. that we pop all the other data off the top of the stack so that
  277. the INT address is the top frame.
  278.      When we jump into the old ISR routine without anything
  279. except the INT ADDR and the FLAGS, the IRET instruction it
  280. executes will take the CPU back to the original code which called
  281. the INT in the first place. Here is a schematic describing the
  282. events that our ISR goes through while it's chained through
  283. interrupts:
  284.  
  285. -----------------------------------------------------------------
  286. --
  287.  
  288. MOV  AH,40h
  289. INT  21h         ; Code executing in the foreground
  290.       |
  291.       \-------------\    ;The INT 21h looks at the IVT to find
  292. the
  293.                     |    ; interrupt vector for 21h
  294.                     |
  295. <...--+--+--+--+--+--+--+--+--+--...>
  296.     16|17|18|19|20|21|22|23|24|25     ;Interrupt Vector
  297. Table(IVT)
  298. <...--+--+--+--+--+--+--+--+--+--...> ;The IVT points to our ISR
  299.  
  300.                     |
  301.                     |
  302.       /-------------/    ; Jumps directly to our ISR before
  303.       |                  ; DOS gets control...
  304.       |
  305. Interrupt 21h entrypoint:
  306. Save all registers and flags
  307. IF buffer is empty jump to end
  308. Encrypt keystrokes in buffer
  309. Open file for output
  310. Move file pointer to end of file
  311. Write our keystrokes to file
  312. Close file
  313. IF we've been called from interrupt 16h, return there
  314. end:
  315. Restore all registers and flags
  316. Jump to old interrupt 21h ISR
  317.       |
  318.       |           ; This code is what jumps into the DOS routine
  319. that
  320.       |           ; actually handles the work to be done by INT 21h
  321.       |           ; At this point, the stack only has the return
  322.       |           ; address of the calling code(MOV AH,40h: INT 21h)
  323.       |
  324. [DOS INT 21h code]
  325. PUSH           DS
  326. ..
  327. ..
  328. POP  DS
  329. IRET                  ; Here the DOS INT 21h code pulls the original
  330.      |             ; address of the calling code and returns to
  331.      |             ; executing the foreground program that called it
  332.      |
  333.      \------------------------------------------------------\
  334.                                                                                                                                                                                                                                                                                                                                                                                                       |
  335. MOV  AH,40h                                                                                                                                                                                                                                                                         |
  336. INT  21h         ; Original code executing in the foreground|
  337. JC   @write_error <-----------------------------------------/
  338.  
  339.                               ; System jumps directly back into the calling code
  340.                               ; after the interrupt was called.
  341.  
  342. -----------------------------------------------------------------
  343. --
  344.  
  345. Our interrupt 16h handlers works very similarly to this one.
  346. However, with the interrupt 16h handler, we don't have to do save
  347. the flags, because they aren't modified by the BIOS interrupt.
  348. INT 21h does modify the flags(sets carry flag to indicate
  349. failure).
  350.      Now that we've figured out the majority of our handler
  351. routines, we have to do a couple more things:
  352. 1. Install our handlers in memory
  353. 2. Encrypt our code and data to make it less suspicious to the
  354. casual observer.
  355. 3. Hide our code in memory so it doesn't show up in normal 'mem'
  356. scans.
  357. 4. Make our save file invisible to the user.
  358.  
  359.      Let's figure out first how we install our code. I wrote the
  360. program as a COM file, meaning that there is only one segment to
  361. deal with. Once our code is in memory, all we have to do is leave
  362. it there, but return back to DOS. I do it using int 27h, leaving
  363. the ISR's and the key buffer in memory. Of course, our
  364. installation code also has to redirect the far pointers in the
  365. IVT to point to our new handlers, and it has to save the old ones
  366. so we can insert ourselves into the interrupt chain. You can use
  367. DOS interrupts to
  368. redirect the interrupt table, or you can simply modify it
  369. yourself (my preferred method... Don't use interrupts if it's
  370. just as easy to do the work yourself. Makes debugging of your
  371. code more difficult, and it'll work quicker too.) The IVT is
  372. located at segment 0000. To read or write to a certain interrupt,
  373. simply multiply the interrupt number by 4 and use that as the
  374. offset. For instance, interrupt 16h would turn out to be at
  375. 0000:0058h (16h*4). From there it's easy to save the old
  376. interrupt and install the new one. First, move the old interrupt
  377. far pointer into a variable you've defined in your program. Then,
  378. once you have your old interrupt vector saved, install your new
  379. one, pointing to the spot in memory where you have your new ISR.
  380. This gives us control of the interrupts 16h and 21h every time
  381. they're called from now on. When your ISR is called, you simply
  382. execute the code you want (save the keystroke), and pass control
  383. back to the original handler that you have saved in your far
  384. pointer variable.
  385.      Now that we know how to install our ISR code, we have to
  386. write a program that will encrypt our code to make it less
  387. obvious. If there's a bunch of file names in the .COM file, it'll
  388. be pretty obvious what files the program deals with. We don't
  389. want our program to be that conspicuous, it'll give away what's
  390. going on way too quickly if it is. For instance, the file name
  391. that the keystrokes will be saved has to be specified explicitly.
  392. Finding it after you know where to look won't take much time,
  393. despite the fact that we hide it from view pretty cleverly later.
  394. So, we'll make it much harder to find the file names in the
  395. executable by encrypting the first portion of the code. Any form
  396. of encryption will do, since all it has to do is make it so the
  397. data we have at the beginning of our program doesn't show to the
  398. average user. A few encryption routines are easy to implement on
  399. the byte level; I chose to NOT each byte. I could have used an
  400. XOR encryption, since it's just as simple, but the code for NOT
  401. is a little easier and smaller. Besides, doing a NOT is exactly
  402. the same as XOR'ing a byte with 0FFh anyway. The NOT instruction
  403. is slightly smaller in memory though, so I used it. Assuming
  404. standard .COM TSR setup, our data is stored at the beginning of
  405. the program, and our installation code is stored near the end, so
  406. I decided to simply encrypt the first half of the program along
  407. with the data. It takes no more code to do that, and it really
  408. doesn't take much more time to decrypt the code before installing
  409. it anyway. It makes disassembling it pretty difficult until you
  410. figure out that it's encrypted. After that though, it shouldn't
  411. be too difficult to pull apart. It will stand up to a
  412. substantially more than casual lookover though. Most sysadmins
  413. and the like won't spend quite the amount of time required to
  414. disassemble the code if they're not even sure about whether a
  415. program is a culprit. A single trivial bit of code can be used to
  416. encrypt both our program, as well as our keystrokes as we save
  417. them to disk.
  418.      Now that we have our program encrypted as well as installed
  419. in memory somewhere, we have to find some way to hide it in
  420. memory. After pulling my hair out over this for a while, I
  421. finally came up with a great solution. I'm sure some virus guys
  422. know good tricks for hiding stuff in memory, but I couldn't get
  423. any answers from them. I thought of everything from tacking my
  424. code onto the end of a segment of code in memory owned by another
  425. program. That wouldn't work because if the program wasn't
  426. permanently resident, it would deallocate the memory I wanted and
  427. write over it later, thereby crashing your system. I considered
  428. not actually allocating it. Same problem as before, it would
  429. overwrite my code: crash. I thought about dropping the amount of
  430. available memory using the BIOS data segment. Kinda obvious in
  431. memory scans, and I couldn't be sure that the top of memory would
  432. be free. This works in boot sector loaders, but not after DOS has
  433. loaded. Eventually I started reading DOS internals books until I
  434. found a section describing how DOS actually allocates memory. It
  435. uses a structure called a Memory Control Block (MCB) to assign
  436. memory to different processes. Every single chunk of memory DOS
  437. allocates has an accompanying MCB to describe it. An MCB is a 16
  438. byte (one paragraph structure). It is always located in the
  439. segment before the segment that it describes. For instance if you
  440. allocate memory at segment 0601h, the Memory Control Block which
  441. describes it can be found at segment 0600h.
  442.      The first byte of an MCB is either an M or a Z. If it's an
  443. M, it means that it's not the last MCB, where a Z denotes the
  444. last in the MCB chain. For those who know data structures, it's a
  445. singly linked list. This means you can only run the chain one
  446. direction. You have to be able to find the first MCB and trace
  447. your way through it to the end to see them all. You keep reading
  448. MCB's until you hit one that has a Z as the first character. The
  449. second field in the MCB is the owner block. It is the segment
  450. that the MCB belongs to. For a typical block, it points to the
  451. segment directly following it. There are exceptions, but we'll
  452. get to that later. If you're tracing the chain, this is how you
  453. know that a given MCB is pointing to code you own, since the
  454. owner will be equal to your code segment (CS register). We'll
  455. need this info later to find MCB's that we own in memory so we
  456. can modify them to our heart's desire. Next field: the number of
  457. paragraphs in the code segment following it. Each paragraph is 16
  458. bytes long. Using the length field, we can determine the position
  459. in memory of the next Memory Control Block. The unused portion of
  460. the MCB is useless to you unless you want to store interesting
  461. information there. DOS basically leaves it completely alone. The
  462. program name contains an ASCIIZ string of the name of the file
  463. you loaded. It's the easiest way for programs like 'mem' to find
  464. out who a memory block belongs to. I'm going to use it as part of
  465. the way to hide our block.
  466.  
  467. Typical Memory Control Block:
  468. -----------------------------
  469.  
  470. Done?     Owner     Length    Unused    Program name
  471. ------    -------   -------   -------   ------------
  472. 1 byte    2 bytes   2 bytes   3 bytes   8 bytes
  473. ------    -------   -------   -------   ------------
  474. 04Dh      0xxxxh                 "FILENAME"
  475.  
  476.      Armed with this knowledge, I set out to find how I could
  477. make my code invisible to a user, yet set it as an unusable space
  478. for DOS so it wouldn't be overwritten. It stumped me for about
  479. two days until I decided to find how DOS allocates space for it's
  480. own kernel. I found that there are two ways of making DOS think
  481. that your code is actually part of the operating system. There
  482. are two DOS specifications for OS kernel memory. The first is for
  483. DOS code, and the other is DOS data. The first part of giving our
  484. code to DOS is to mark the owner as number 8. DOS always marks
  485. it's own code as process number 8 for some unknown reason. Then,
  486. in the program name field we put an 'SC' in the first field two
  487. bytes of the program name field to tell DOS that it's a system
  488. code segment. For a Data segment, we put an 'SD' in the first two
  489. bytes. Now, when DOS does memory scans, it won't decide to write
  490. over our code because it's still marked as in use, yet it won't
  491. show up as a separate program, since the DOS kernel is flagged as
  492. a single entity even if there are several segments that are
  493. marked as DOS code.
  494.  
  495. DOS Memory Control Block:
  496. -------------------------
  497.  
  498. Done?     Owner     Length    Unused    Program name
  499. ------    -------   -------   -------   ------------
  500. 1 byte    2 bytes   2 bytes   3 bytes   8 bytes
  501. ------    -------   -------   -------   ------------
  502. 04Dh      0008h     0xxxxh              "CS      "
  503.  
  504.      This is a sample MCB as it would probably look in memory.
  505. For most programs, two separate segments are created though, and
  506. we want to make sure that we set both of them as DOS code, or
  507. they will show up in a memory scan. The second segment that is
  508. created is the environment block. To make sure that we get all
  509. the segments that are created for our program, we'll simply walk
  510. the entire MCB chain when we start up and mark any MCB's that we
  511. happen to own as owned by DOS instead. Typically, when a program
  512. is loaded by DOS, it's environment block is located directly
  513. before the program segment is, and it would be easy to find by
  514. moving back in memory a few segments. This would certainly be
  515. easier than reading the entire MCB block in sequence, and would
  516. certainly be faster too. The problem with this is when you load a
  517. program high with a 3rd party memory manager such as QEMM or 386-
  518. MAX. They don't guarantee that your environment block will be
  519. located right before your program while in high memory. It will
  520. try to fit programs wherever they can in the HMA. Searching all
  521. the MCB's in order will ensure that we get all the MCB's that are
  522. allocated for us, without depending at all on where they are
  523. located in memory. Very neat solution. If a file is loaded low,
  524. 'mem' will show nothing, unless you use the debug option: 'mem
  525. /d':
  526.  
  527. -----------------------------------------------------------------
  528. -
  529.  
  530. Conventional Memory Detail:
  531.  
  532. Segment               Total        Name         Type
  533. -------       ----------------  -----------  --------
  534.  00000           1,039    (1K)               Interrupt Vector
  535.  00040             271    (0K)               ROM Communication
  536. Area
  537.  00050             527    (1K)               DOS Communication
  538. Area
  539.  00070           2,752    (3K)  IO           System Data
  540.                                    CON       System Device Driver
  541.                                    AUX       System Device Driver
  542.                                    PRN       System Device Driver
  543.                                    CLOCK$    System Device Driver
  544.                                    A: - D:   System Device Driver
  545.                                    COM1      System Device Driver
  546.                                    LPT1      System Device Driver
  547.                                    LPT2      System Device Driver
  548.                                    LPT3      System Device Driver
  549.                                    COM2      System Device Driver
  550.                                    COM3      System Device Driver
  551.                                    COM4      System Device Driver
  552.  0011C           5,104    (5K)  MSDOS        System Data
  553.  0025B          10,704   (10K)  IO           System Data
  554.                  3,536    (3K)     NU-MEGA   Installed
  555. Device=S-ICE   
  556.                  1,152    (1K)     XMSXXXX0  Installed
  557. Device=HIMEM   
  558.                  1,488    (1K)               FILES=30
  559.                    256    (0K)               FCBS=4
  560.                    512    (1K)               BUFFERS=40
  561.                    624    (1K)               LASTDRIVE=G
  562.                  3,008    (3K)               STACKS=9,256
  563.  004F8              80    (0K)  MSDOS        System Program
  564.  004FD              64    (0K)  COMMAND      Data
  565.  00501           2,656    (3K)  COMMAND      Program
  566.  005A7              80    (0K)  MSDOS        -- Free --
  567.  005AC             272    (0K)  COMMAND      Environment
  568.  005BD             192    (0K)  MSDOS        -- Free --
  569.  005C9          32,096   (31K)  SMARTDRV     Program
  570. *00D9F             240    (0K)  MSDOS        System Program
  571. *00DAE           1,168    (1K)  MSDOS        System Program
  572.  00DF7             240    (0K)  MEM          Environment
  573.  00E06          88,992   (87K)  MEM          Program
  574.  023C0         507,904  (496K)  MSDOS        -- Free --
  575.  
  576. Memory Summary:
  577.  
  578.   Type of Memory       Total   =    Used    +    Free
  579.   ----------------  ----------   ----------   ----------
  580.   Conventional         654,336       57,168      597,168
  581.   Upper                      0            0            0
  582.   Reserved                   0            0            0
  583.   Extended (XMS)    15,122,432    4,259,840   10,862,592
  584.   ----------------  ----------   ----------   ----------
  585.   Total memory      15,776,768    4,317,008   11,459,760
  586.  
  587.   Total under 1 MB     654,336       57,168      597,168
  588.  
  589.   Memory accessible using Int 15h              0     (0K)
  590.   Largest executable program size        596,880   (583K)
  591.   Largest free upper memory block              0     (0K)
  592.   MS-DOS is resident in the high memory area.
  593.  
  594.   XMS version  3.00; driver version  3.16
  595. * Sections of code that are owned by our keystroke recorder.
  596.   Larger section is the code, and the smaller 240 byte block
  597.   is the environment block. Still pretty unnoticeable, because
  598.   it's attributed to the Operating System.
  599. -----------------------------------------------------------------
  600. -
  601.  
  602. If we manage to load our code high however, it's completely
  603. invisible to mem even with the debug options on:
  604.  
  605. -----------------------------------------------------------------
  606. -
  607.  
  608.  
  609. Conventional Memory Detail:
  610.  
  611. Segment               Total        Name         Type
  612. -------       ----------------  -----------  --------
  613.  00000           1,039    (1K)               Interrupt Vector
  614.  00040             271    (0K)               ROM Communication
  615. Area
  616.  00050             527    (1K)               DOS Communication
  617. Area
  618.  00070           2,752    (3K)  IO           System Data
  619.                                    CON       System Device Driver
  620.                                    AUX       System Device Driver
  621.                                    PRN       System Device Driver
  622.                                    CLOCK$    System Device Driver
  623.                                    A: - D:   System Device Driver
  624.                                    COM1      System Device Driver
  625.                                    LPT1      System Device Driver
  626.                                    LPT2      System Device Driver
  627.                                    LPT3      System Device Driver
  628.                                    COM2      System Device Driver
  629.                                    COM3      System Device Driver
  630.                                    COM4      System Device Driver
  631.  0011C           5,104    (5K)  MSDOS        System Data
  632.  0025B          10,272   (10K)  IO           System Data
  633.                  1,152    (1K)     XMSXXXX0  Installed
  634. Device=HIMEM   
  635.                  3,104    (3K)     EMMXXXX0  Installed
  636. Device=EMM386  
  637.                  1,488    (1K)               FILES=30
  638.                    256    (0K)               FCBS=4
  639.                    512    (1K)               BUFFERS=40
  640.                    624    (1K)               LASTDRIVE=G
  641.                  3,008    (3K)               STACKS=9,256
  642.  004DD              80    (0K)  MSDOS        System Program
  643.  004E2              64    (0K)  COMMAND      Data
  644.  004E6           2,656    (3K)  COMMAND      Program
  645.  0058C              80    (0K)  MSDOS        -- Free --
  646.  00591             272    (0K)  COMMAND      Environment
  647.  005A2          17,056   (17K)  SMARTDRV     Program
  648.  009CC             240    (0K)  MEM          Environment
  649.  009DB          88,992   (87K)  MEM          Program
  650.  01F95         524,960  (513K)  MSDOS        -- Free --
  651. Upper Memory Detail:
  652.  
  653. Segment  Region       Total        Name         Type
  654. -------  ------  --------------  --------  --------------
  655.  0C94A       1   14,496   (14K)  IO           System Data
  656.                  10,256   (10K)  MSCD001   Device=SBPCD   
  657.                   4,192    (4K)  CON       Device=ANSI    
  658.  
  659.  0CD24       3      112    (0K)  MSDOS     -- Free --
  660.  0CD2B       3   15,152   (15K)  MOUSE     Program
  661.  0D0DE       3   17,904   (17K)  SHARE     Program
  662.  0D53D       3   16,432   (16K)  MSCDEX    Program
  663.  0D940       3      192    (0K)  MSDOS     -- Free --
  664.  0D94C       3   16,400   (16K)  SMARTDRV  Data
  665.  0DD4D       3   11,056   (11K)  MSDOS     -- Free --
  666. Memory Summary:
  667.  
  668.   Type of Memory       Total   =    Used    +    Free
  669.   ----------------  ----------   ----------   ----------
  670.   Conventional         654,336       40,304      614,032
  671.   Upper                 91,776       80,416       11,360
  672.   Reserved                   0            0            0
  673.   Extended (XMS)*   15,636,864    4,708,736   10,928,128
  674.   ----------------  ----------   ----------   ----------
  675.   Total memory      16,382,976    4,829,456   11,553,520
  676.  
  677.   Total under 1 MB     746,112      120,720      625,392
  678.  
  679.  
  680.   Handle      EMS Name      Size   
  681.   -------     --------     ------  
  682.         0                  060000
  683.         1                  018000
  684.  
  685.   Total Expanded (EMS)                16,056,320 (15,680K
  686.   Free Expanded (EMS)*                11,075,584 (10,816K
  687.  
  688.   * EMM386 is using XMS memory to simulate EMS memory as needed.
  689.     Free EMS memory may change as free XMS memory changes.
  690.  
  691.   Memory accessible using Int 15h              0     (0K)
  692.   Largest executable program size        613,936   (600K)
  693.   Largest free upper memory block         11,056    (11K)
  694.   MS-DOS is resident in the high memory area.
  695.  
  696.   XMS version  3.00; driver version  3.16
  697.   EMS version  4.00
  698.  
  699. * You'll notice that our program shows NOWHERE in this listing.
  700.   Although it is installed in memory in the UMB's it's not shown!
  701.   Pretty handy eh?
  702. -----------------------------------------------------------------
  703. -
  704.  
  705. You can find the code using MSD even if it's installed high, but
  706. it shows up as 'Excluded UMB area'. Often there are several such
  707. areas besides the ones created by our program, so it blends in
  708. pretty well. Once again, some pretty close scrutiny would be
  709. shrugged off by our program when it's properly installed.
  710.      Where are we now? We've got our program decrypted off the
  711. disk, installed in memory, and hidden there from users. What else
  712. is there to do? I've implemented one last step to hide the
  713. effects of our program. I've made it very difficult to find the
  714. file that the recorder uses to save keystrokes to. Since we're
  715. trapping Interrupt 21h anyway, I decided to intercept the
  716. directory interrupts specifically. When a directory scan gets to
  717. the file that we have designated as our keystroke saving program,
  718. it won't display it. What does this mean? It means that you can't
  719. find the file on disk by doing a directory scan. The file still
  720. exists and most file operations work on it. For instance, you can
  721. edit it, delete it, copy it, move it and so on. You simply can't
  722. do any searches for it with normal DOS functions. Using 3rd party
  723. tools(such as Norton Disk-Edit) works for finding it, since they
  724. read the hardware directly with BIOS functions, bypassing DOS
  725. completely. The downside of this is that some disk utility
  726. programs will get upset about the file not showing up. CHKDSK has
  727. no problems with it, and SCANDISK doesn't touch it either. Also,
  728. since I didn't put in any code that checks what directory the
  729. system is currently scanning, if there are files in other
  730. directories with the same filenames as the one you're looking at,
  731. they will be hidden too. For instance, if you're saving
  732. keystrokes to a file named 'data'. If you have another file named
  733. 'data' in another directory, it will be invisible too. Because of
  734. this, you should name your save file something obscure that no
  735. other files use. Name it something like 'abzzzzzz.tmp' so it
  736. looks like a DOS temp file, which will go both unnoticed, and is
  737. unlikely to be duplicated elsewhere.
  738.      The way we get DOS to miss our file name is by grabbing the
  739. 4 interrupts that search for files(11h, 12h, 4Eh, 4Fh.) If those
  740. interrupts are going to find our file name, we simply have DOS
  741. scan the directory for the next file instead. To do this, we have
  742. to find the current DTA, which is where DOS passes back the info
  743. about the current directory scan. Then we have to check to see if
  744. our file name is in there. If it is, we call the FindNextMatch
  745. DOS function instead of returning our file name.
  746.      Deploying this program is going to be your toughest
  747. challenge. It's easy to detect it if you know where to look. It
  748. has to be in
  749. your autoexec.bat somewhere if you want it to be loaded every
  750. time that the computer boots. There are a few ways of making your
  751. recorder much more stealthy. The first would be to name it
  752. something that many people use such as 'SETVER' or 'DOSKEY' any
  753. other utility that lots of people might put into their
  754. autoexec.bat. If you want to put it into the Windows directory,
  755. you can throw some text into the program to make it look like
  756. it's actually part of windows. For instance, put some text saying
  757. 'Windows Protected Mode', or something about .DLL's.
  758.      A better solution would be to make this into a boot sector
  759. program. You can use many of the same techniques that virii do to
  760. hide yourself from sight even better. If it's a boot sector
  761. virus, it actually gets loaded before the Operating System does,
  762. which has it's ups and downs. It means that it's totally
  763. invisible to the user, but it also makes it more difficult to
  764. install the program, since you can't just rely upon DOS
  765. interrupts to allocate memory for your program. Also, if you wish
  766. to revector DOS interrupts, even though you're installing at boot
  767. time, you have to redirect them after DOS is done loading. This
  768. means you have to install your code into memory after BIOS is
  769. done with it's POST(Power On Self Test), but you can't modify the
  770. DOS interrupt vectors until after DOS sets them. I can't go into
  771. DOS boot sector stuff yet, since I haven't gotten it to work on
  772. my system yet. I've crashed my system countless times, but
  773. haven't managed to finish it. I hope you learned a little
  774. something from this article, I certainly learned a lot from
  775. making KEYS.
  776.  
  777.      Shortcomings of KEYS:
  778. 1. Doesn't read input in Windows. Windows has it's own keyboard
  779. driver, which bypasses the DOS INT 16h and INT 9h handlers
  780. altogether. To read input in Windows takes much more serious
  781. work. If you're really interested, let me know. Most passwords
  782. and the like are typed in at the commandline anyway(Network
  783. logins etc.) Essentially, any program that does it's own keyboard
  784. work won't let keys capture keystrokes(most arcade style games
  785. won't use int 16)
  786. 2. That's about it...
  787.  
  788.      At first I wasn't sure whether I was going to release my
  789. version of KEYS into the general public. However, I decided it's
  790. something lots of people want to have, and it's pretty difficult
  791. to make. It's just a tool, and one that not everyone will have
  792. access to. Also, I included the source so others could learn from
  793. what I did. It doesn't have lots of copyright stuff on it, and
  794. the code has no splash screens or other useless tags saying I
  795. wrote it. So, what you do with it is your choice. I can't, and
  796. don't care about stopping you from claiming it as your own code
  797. or whatever. I only ask that if you use it and find it useful
  798. that you send me some mail telling me how you used it and how
  799. wonderful I am for giving it out. Any feedback I get is great and
  800. makes me more willing to write freeware apps for the public in
  801. the future. That's it, enjoy!
  802.      KEYS should work on just about any IBM compatible, as low as
  803. an 8088, and with DOS version 2.0 or higher. Basically any setup
  804. will work out fine for KEYS. On slower systems there might be a
  805. noticeable lag, but I don't know. All I have to test it on are
  806. 486's...
  807.      Instructions for compiling KEYS:
  808.      I wrote KEYS to work with the A86 assembler, version 3.71.
  809. First off, when the assembler is finished assembling the KEYS
  810. source, it's not in encrypted form yet. In order for it to work,
  811. it HAS to be encrypted. If it's not, the loading function will
  812. try to decrypt unencrypted code(bad...) So, you have to run the
  813. program 'ENCODE.EXE' in the same directory as 'KEYS.COM'. It will
  814. spit out the encrypted program 'KEYS2.COM'. If it turns out that
  815. you decide to change KEYS, you may need to change the decryption
  816. program to reflect that. To figure out the length of needed
  817. encryption, simply load up the unencrypted, freshly assembled
  818. version of KEYS and in a debugger. The fourth line of code that's
  819. executed is the length you have to encrypt to. For instance, in
  820. Turbo Debugger, load the program and hit 'F8' 3 times. The line
  821. you're on will be:
  822. MOV  CX,[something]
  823. The something is what you have to put as the constant at the top
  824. of 'ENCODE.C'(originally 0x16F.) To configure KEYS, simply run
  825. it's accompanying configuration program, 'KONFIG.EXE' with the
  826. 'KEYS.COM' file in the current directory. It'll ask you what the
  827. full path of the save file is you want to use. Konfig works only
  828. on the already encrypted version of KEYS. To decrypt your info,
  829. simply run 'DEC.EXE' with an input file name, and an output file
  830. name.
  831. For instance:
  832. DEC C:\DOS\ABZZABZZ.TMP KEYSTROK.DAT
  833.  
  834.      Keep in mind that you can still access the file when it's on
  835. a system that you can't see it on. For instance, if you're
  836. running it on one of your school computers, you can simply copy
  837. the file from the hard drive to a floppy without decrypting it,
  838. or even being able to see it. So even if your admin sees you
  839. trying to copy a file, he won't be able to figure out where it
  840. is, or even if he gets the file somehow, it's encrypted anyhow.
  841. You're pretty safe here unless you get really stupid, which I
  842. don't discount as a possibility.
  843. That ought to do ya. Good luck with it...
  844.  
  845. Zephyr
  846.